
#include "bsp.h"


#define SPI_CS_0()   gpio_bit_reset(GPIOC, GPIO_PIN_0)
#define SPI_CS_1()   gpio_bit_set(GPIOC, GPIO_PIN_0)

#define DUMMY_BYTE          0xFF



/* 芯片寄存器相关的宏 */
#define CMD_READ_JEDEC_ID          0x9F  /* 读芯片JEDE ID  */
#define CMD_READ_DEVICE_ID         0xAB  /* 读芯片DEVICE ID */
#define CMD_WRITE_ENABLE           0x06  /* 写使能 */
#define CMD_READ_STATUS_REG1       0x05  /* 读状态寄存器1 */
#define CMD_READ_STATUS_REG2       0x35  /* 读状态寄存器2 */
#define CMD_READ_STATUS_REG3       0x15  /* 读状态寄存器3 */
#define CMD_WRITE_STATUS_REG1      0x01  /* 写状态寄存器1 */
#define CMD_WRITE_STATUS_REG2      0x31  /* 写状态寄存器2 */
#define CMD_WRITE_STATUS_REG3      0x11  /* 写状态寄存器3 */
#define CMD_PAGE_PROGRAM           0x02  /* 页编程命令 */
#define CMD_READ_DATA              0x03  /* 读数据命令 */
#define CMD_ERASE_4K               0x20  /* 擦除4K字节（一个扇区）*/
#define CMD_ERASE_CHIP             0xC7  /* 擦除整个芯片 */
#define CMD_READ_MANUFACT_DEV_ID   0x90  /* （读制造商ID） */
#define CMD_EANBLE_4BYTE_ADDR      0xB7  /* 4字节地址 */

#define FLASH_STATUS_REG1_BUSY            0x01


/* 芯片制造商ID */
#define GD25Q16_MANUFACT_DEV_ID     0x14C8
#define GD25Q128_MANUFACT_DEV_ID    0x17C8  /* 这个值没确定 */ 

#define W25Q80_MANUFACT_DEV_ID      0XEF13  /* W25Q80  芯片制造商ID */
#define W25Q16_MANUFACT_DEV_ID      0XEF14  /* W25Q16  芯片制造商ID */
#define W25Q32_MANUFACT_DEV_ID      0XEF15  /* W25Q32  芯片制造商ID */
#define W25Q64_MANUFACT_DEV_ID      0XEF16  /* W25Q64  芯片制造商ID */
#define W25Q128_MANUFACT_DEV_ID     0XEF17  /* W25Q128 芯片制造商ID */
#define W25Q256_MANUFACT_DEV_ID     0XEF18  /* W25Q256 芯片制造商ID */

static spi_flash_info_t _flash;

static uint8_t _flash_buf[SPI_FLASH_BUFF_SIZE];   /* 扇区缓存 */

/***************************************************************************************************
 * @fn      flash_rw_byte
 *
 * @brief   flash底层接口：读写译一个字节数据
 *
 * @param   write 从spi接口写入的数据
 *
 * @return  从spi接口读到的数据
 */
static uint8_t flash_rw_byte(uint8_t write)
{
    return spi_rw_byte(write);
}

/***************************************************************************************************
 * @fn      spi_flash_read_status_reg
 *
 * @brief   读Flash状态寄存器
 * 
 * @param   reg_n 寄存器编号
 *
 * @note        状态寄存器1：
 *              BIT7  6   5   4   3   2   1   0
 *              SPR   RV  TB BP2 BP1 BP0 WEL BUSY
 *              SPR:默认0,状态寄存器保护位,配合WP使用
 *              TB,BP2,BP1,BP0:FLASH区域写保护设置
 *              WEL:写使能锁定
 *              BUSY:忙标记位(1,忙;0,空闲)
 *              默认:0x00
 *
 *              状态寄存器2：
 *              BIT7  6   5   4   3   2   1   0
 *              SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
 *
 *              状态寄存器3：
 *              BIT7      6    5    4   3   2   1   0
 *              HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
 * 
 * @return  状态寄存器值
 */
static uint8_t spi_flash_read_status_reg(reg_num_e reg_n)
{
    uint8_t status, cmd;

    switch (reg_n)
    {
        case FLASH_STATUS_REG1:
        {
            cmd = CMD_READ_STATUS_REG1;
            break;
        }

        case FLASH_STATUS_REG2:
        {
            cmd = CMD_READ_STATUS_REG2;
            break;
        }

        case FLASH_STATUS_REG3:
        {
            cmd = CMD_READ_STATUS_REG3;
            break;
        }

        default:
        {
            cmd = CMD_READ_STATUS_REG1;
            break;
        }
    }

    SPI_CS_0();

    /* 发送读状态命令 */
    spi_rw_byte(cmd);

    /* 读取芯片回复的状态 */
    status = spi_rw_byte(DUMMY_BYTE);

    SPI_CS_1();

    return status;
}

/***************************************************************************************************
 * @fn      spi_flash_write_status_reg
 *
 * @brief   写Flash状态寄存器
 * 
 * @param   reg_n 寄存器编号
 * @param   status 写入的状态数据
 *
 * @note        状态寄存器1：
 *              BIT7  6   5   4   3   2   1   0
 *              SPR   RV  TB BP2 BP1 BP0 WEL BUSY
 *              SPR:默认0,状态寄存器保护位,配合WP使用
 *              TB,BP2,BP1,BP0:FLASH区域写保护设置
 *              WEL:写使能锁定
 *              BUSY:忙标记位(1,忙;0,空闲)
 *              默认:0x00
 *
 *              状态寄存器2：
 *              BIT7  6   5   4   3   2   1   0
 *              SUS   CMP LB3 LB2 LB1 (R) QE  SRP1
 *
 *              状态寄存器3：
 *              BIT7      6    5    4   3   2   1   0
 *              HOLD/RST  DRV1 DRV0 (R) (R) WPS ADP ADS
 * 
 * @return  状态寄存器值
 */
void spi_flash_write_status_reg(reg_num_e reg_n, uint8_t status)
{
    uint8_t cmd;

    switch (reg_n)
    {
        case FLASH_STATUS_REG1:
        {
            cmd = CMD_WRITE_STATUS_REG1;
            break;
        }

        case FLASH_STATUS_REG2:
        {
            cmd = CMD_WRITE_STATUS_REG2;
            break;
        }

        case FLASH_STATUS_REG3:
        {
            cmd = CMD_WRITE_STATUS_REG3;
            break;
        }

        default:
        {
            cmd = CMD_WRITE_STATUS_REG1;
            break;
        }
    }

    SPI_CS_0();

    /* 发送读状态命令 */
    spi_rw_byte(cmd);

    /* 写入状态数据 */
    spi_rw_byte(status);

    SPI_CS_1();
}

/***************************************************************************************************
 * @fn      flash_send_addr
 *
 * @brief   发送地址到Flash
 *
 * @param   write 从spi接口写入的数据
 *
 * @return  从spi接口读到的数据
 */
static void flash_send_addr(uint32_t addr)
{
    /* 大容量型号要多发一个字节的地址数据 */
    if (GD25Q128_MANUFACT_DEV_ID == _flash.manufact_dev_id)
    {
        flash_rw_byte((uint8_t)((addr >> 24) & 0xFF));
    }

    flash_rw_byte((uint8_t)((addr >> 16) & 0xFF));
    flash_rw_byte((uint8_t)((addr >> 8) & 0xFF));
    flash_rw_byte((uint8_t)(addr & 0xFF));
}

/***************************************************************************************************
 * @fn      spi_flash_write_enable
 *
 * @brief   flash写使能
 *
 * @param   无
 *
 * @return  无
 */
static void spi_flash_write_enable(void)
{
    SPI_CS_0();

    /* 发送写使能命令 */
    flash_rw_byte(CMD_WRITE_ENABLE);

    SPI_CS_1();
}

/***************************************************************************************************
 * @fn      spi_flash_wait_for_write_end
 *
 * @brief   等待Flash写完成
 *
 * @param   无
 *
 * @return  无
 */
static void spi_flash_wait_for_write_end(void)
{
    uint8_t status;

    do
    {
        status = spi_flash_read_status_reg(FLASH_STATUS_REG1);
    } while ((status & FLASH_STATUS_REG1_BUSY) != 0);
}

/***************************************************************************************************
 * @fn      spi_flash_write_page
 *
 * @brief   Flash写一页数据
 *
 * @param   addr 只能是每一页的首地址
 * @param   buff 写入的数据
 * @param   buff_len 数据长度（单位：字节）
 *
 * @return  无
 */
void spi_flash_write_page(uint32_t addr, uint8_t *buff, uint32_t buff_len)
{
    /* 写使能 */
    spi_flash_write_enable();

    SPI_CS_0();

    /* 发送页编程命令 */
    flash_rw_byte(CMD_PAGE_PROGRAM);

    /* 发送读取的地址 */
    flash_send_addr(addr);

    while (buff_len--)
    {
        flash_rw_byte(*buff);
        buff++;
    }

    SPI_CS_1();

    /* 等待写操作完成 */
    spi_flash_wait_for_write_end();
}

/***************************************************************************************************
 * @fn      spi_flash_write_nocheck
 *
 * @brief   Flash写一页数据
 *
 * @param   addr 只能是每一页的首地址
 * @param   buff 写入的数据
 * @param   buff_len 数据长度（单位：字节）
 *
 * @return  无
 */
void spi_flash_write_nocheck(uint32_t addr, uint8_t *buff, uint32_t data_len)
{
    uint16_t page_remain;

    /* 单页剩余的字节数 */
    page_remain = _flash.page_size - addr % _flash.page_size;  

    /* 不大于256个字节 */
    if (data_len <= page_remain)      
    {
        page_remain = data_len;
    }

    while (1)
    {
        /* 当写入字节比页内剩余地址还少的时候, 一次性写完
         * 当写入直接比页内剩余地址还多的时候, 先写完整个页内剩余地址, 然后根据剩余长度进行不同处理
         */
        spi_flash_write_page(addr, buff, page_remain);
        
        /* 写入结束了 */
        if (data_len == page_remain)      
        {
            break;  /* 退出 */
        }
        else  /* data_len > page_remain */
        {
            buff += page_remain;         /* pbuf指针地址偏移,前面已经写了pageremain字节 */
            addr += page_remain;         /* 写地址偏移,前面已经写了pageremain字节 */
            data_len -= page_remain;      /* 写入总长度减去已经写入了的字节数 */

            if (data_len > _flash.page_size)          /* 剩余数据还大于一页,可以一次写一页 */
            {
                page_remain = _flash.page_size;       /* 一次可以写入256个字节 */
            }
            else                        /* 剩余数据小于一页,可以一次写完 */
            {
                page_remain = data_len;   /* 不够256个字节了 */
            }
        }
    }
}

/***************************************************************************************************
 * @fn      spi_flash_erase_4k
 *
 * @brief   擦除4K
 *
 * @param   addr 扇区地址
 *
 * @return  无
 */
void spi_flash_erase_4k(uint32_t addr)
{
    /* 写使能 */
    spi_flash_write_enable();

    SPI_CS_0();

    /* 发送4K擦除命令 */
    flash_rw_byte(CMD_ERASE_4K);

    /* 发送扇区地址 */
    flash_send_addr(addr);

    SPI_CS_1();

    /* 等待写操作完成 */
    spi_flash_wait_for_write_end();
}

/***************************************************************************************************
 * @fn      spi_flash_erase_chip
 *
 * @brief   擦除整个芯片
 *
 * @param   无
 *
 * @return  无
 */
void spi_flash_erase_chip(void)
{
    /* 写使能 */
    spi_flash_write_enable();

    SPI_CS_0();

    /* 发送擦除芯片命令 */
    flash_rw_byte(CMD_ERASE_CHIP);

    SPI_CS_1();

    /* 等待写操作完成 */
    spi_flash_wait_for_write_end();
}

/***************************************************************************************************
 * @fn      spi_flash_erase_sector_4k
 *
 * @brief   擦除4K
 *
 * @param   sec_n 第n个扇区
 *
 * @return  无
 */
void spi_flash_erase_sector_4k(uint32_t sec_n)
{
    uint32_t sector_addr = sec_n * _flash.sector_size;

    /* 写使能 */
    spi_flash_write_enable();

    SPI_CS_0();

    /* 发送4K擦除命令 */
    flash_rw_byte(CMD_ERASE_4K);

    /* 发送扇区地址 */
    flash_send_addr(sector_addr);

    SPI_CS_1();

    /* 等待写操作完成 */
    spi_flash_wait_for_write_end();
}

/***************************************************************************************************
 * @fn      spi_flash_write
 *
 * @brief   向Flash写入数据
 *
 * @param   addr 地址
 * @param   buff 要写入的数据
 * @param   data_len 数据长度（单位：字节）
 *
 * @return  读取到的芯片制造商ID
 */
void spi_flash_write(uint32_t addr, uint8_t *buff, uint32_t data_len)
{
    uint32_t sec_pos;
    uint16_t sec_off;
    uint32_t sec_remain;
    uint16_t i;
    uint8_t *flash_buff = &_flash_buf[0];

    sec_pos = addr / _flash.sector_size;  /* 扇区编号（第n扇区） */
    sec_off = addr % _flash.sector_size;  /* 在扇区内的偏移 */
    sec_remain = _flash.sector_size - sec_off;  /* 扇区剩余空间大小 */

    //BSP_LOG("ad:%X,nb:%X\r\n", addr, data_len);  /* 测试用 */

    if (data_len <= sec_remain)
    {
        sec_remain = data_len;  /* 不大于4096个字节 */
    }

    while (1)
    {
        spi_flash_read(sec_pos * _flash.sector_size, flash_buff, _flash.sector_size);  /* 读出整个扇区的内容 */

        /* 校验数据 */
        for (i = 0; i < sec_remain; i++)  
        {
            if (flash_buff[sec_off + i] != 0XFF)
            {
                break;  /* 需要擦除, 直接退出for循环 */
            }
        }

        if (i < sec_remain)  /* 需要擦除 */
        {
            spi_flash_erase_sector_4k(sec_pos);  /* 擦除这个扇区 */

            for (i = 0; i < sec_remain; i++)  /* 复制 */
            {
                flash_buff[i + sec_off] = buff[i];
            }

            spi_flash_write_nocheck((sec_pos * _flash.sector_size), flash_buff, _flash.sector_size);  /* 写入整个扇区 */
        }
        else  /* 写已经擦除了的,直接写入扇区剩余区间. */
        {
            spi_flash_write_nocheck(addr, buff, sec_remain);  /* 直接写扇区 */
        }

        if (data_len == sec_remain)  /* 写入结束了 */
        {
            break;  /* 退出 */
        }
        else  /* 写入未结束 */
        {
            sec_pos++;  /* 扇区编号（第n扇区）增1 */
            sec_off = 0;  /* 偏移位置为0 */
 
            buff += sec_remain;  /* 指针偏移 */
            addr += sec_remain;  /* 写地址偏移 */
            data_len -= sec_remain;  /* 字节数递减 */

            if (data_len > _flash.sector_size)
            {
                sec_remain = _flash.sector_size;  /* 下一个扇区还是写不完 */
            }
            else
            {
                sec_remain = data_len;  /* 下一个扇区可以写完了 */
            }
        }
    }
}


/***************************************************************************************************
 * @fn      spi_flash_read_jedec_id
 *
 * @brief   读取芯片JEDEC ID
 *
 * @param   无
 *
 * @return  读取到的芯片JEDEC ID
 */
uint16_t spi_flash_read_jedec_id(void)
{
    uint16_t id = 0;

    SPI_CS_0();

    /* 发送读制造商ID命令 */
    flash_rw_byte(CMD_READ_JEDEC_ID);
    flash_rw_byte(DUMMY_BYTE);

    /* 读取ID */
    id =  flash_rw_byte(DUMMY_BYTE) << 8;
    id |= flash_rw_byte(DUMMY_BYTE);

    SPI_CS_1();

    return id;
}

/***************************************************************************************************
 * @fn      spi_flash_read_buff
 *
 * @brief   从Flash中读取数据
 *
 * @param   addr 读取的地址
 * @param   buff 读取到的数据
 * @param   buf_len 读取到的数据个数（单位：字节）
 *
 * @return  无
 */
void spi_flash_read(uint32_t addr, uint8_t *buff, uint32_t buff_len)
{
    uint32_t i;

    SPI_CS_0();

    /* 发送读数据命令 */
    flash_rw_byte(CMD_READ_DATA);

    /* 发送读取的地址 */
    flash_send_addr(addr);

    /* 读取数据 */
    for (i = 0; i < buff_len; i++)
    {
        buff[i] = flash_rw_byte(DUMMY_BYTE);
    }

    SPI_CS_1();
}

/***************************************************************************************************
 * @fn      spi_flash_read_device_id
 *
 * @brief   读取芯片设备ID
 *
 * @param   无
 *
 * @return  读取到的芯片设备ID
 */
uint8_t spi_flash_read_device_id(void)
{
    uint8_t id = 0;

    SPI_CS_0();

    /* 发送读设备ID命令 */
    flash_rw_byte(CMD_READ_DEVICE_ID);

    flash_rw_byte(DUMMY_BYTE);
    flash_rw_byte(DUMMY_BYTE);
    flash_rw_byte(DUMMY_BYTE);

    /* 读取设备ID */
    id = flash_rw_byte(DUMMY_BYTE);

    SPI_CS_1();

    return id;
}

/***************************************************************************************************
 * @fn      spi_flash_read_manufact_dev_id
 *
 * @brief   读取芯片制造商ID
 *
 * @param   无
 *
 * @return  读取到的芯片制造商ID
 */
uint16_t spi_flash_read_manufact_dev_id(void)
{
    uint16_t id = 0;

    SPI_CS_0();

    /* 发送读设备ID命令 */
    flash_rw_byte(CMD_READ_MANUFACT_DEV_ID);

    flash_rw_byte(DUMMY_BYTE);
    flash_rw_byte(DUMMY_BYTE);
    flash_rw_byte(DUMMY_BYTE);

    /* 读取设备ID */
    id = flash_rw_byte(DUMMY_BYTE) << 8;
    id |= flash_rw_byte(DUMMY_BYTE);

    SPI_CS_1();

    return id;
}

/***************************************************************************************************
 * @fn      spi_flash_information
 *
 * @brief   获取芯片信息
 *
 * @param   info 信息数据
 *
 * @return  TRUE则获取成功，FALSE则获取失败
 */
BOOL spi_flash_information(spi_flash_info_t *info)
{
    BOOL ret = TRUE;

    /* 指针参数不能为空 */
    if (NULL == info)
    {
        return FALSE;
    }

    /* 读取芯片ID */
    info->manufact_dev_id = spi_flash_read_manufact_dev_id();

    /* 根据不同的芯片，给予不同的名字和容量 */
    switch (info->manufact_dev_id)
    {
        case GD25Q16_MANUFACT_DEV_ID:
        {
            strcpy(info->name, "GD25Q16");
            info->page_size   = 256;
            info->sector_size = 4096;
            info->total_size  = 2 * (1024 * 1024);  /* 2MB */ 
            break;
        }

        case GD25Q128_MANUFACT_DEV_ID:
        {
            strcpy(info->name, "GD25Q128");
            info->page_size   = 256;
            info->sector_size = 4096;
            info->total_size  = 16 * (1024 * 1024);  /* 16MB */ 
            break;
        }

        case W25Q80_MANUFACT_DEV_ID:
        {
            break;
        }

        case W25Q16_MANUFACT_DEV_ID:
        {
            break;
        }

        case W25Q32_MANUFACT_DEV_ID:
        {
            break;
        }

        case W25Q64_MANUFACT_DEV_ID:
        {
            break;
        }

        case W25Q128_MANUFACT_DEV_ID:
        {
            break;
        }

        case W25Q256_MANUFACT_DEV_ID:
        {
            

            break;
        }

        default:
        {
            BSP_LOG("ERROR: flash manufact device ID read fault\r\n");
            ret = FALSE;
            break;
        }
    }

    return ret;
}

/***************************************************************************************************
 * @fn      spi_flash_init
 *
 * @brief   初始化SPI接口
 *
 * @param   无
 *
 * @return  无
 */
BOOL spi_flash_init(void)
{
    uint8_t status_reg;
    uint8_t  device_id = 0;
    uint16_t jede_id = 0;
    BOOL ret = TRUE;
    
    //使能RCU相关时钟
    rcu_periph_clock_enable(RCU_GPIOC);  

    //配置PC0  SPI_CS
    gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_0);  //上拉输出
    gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_0);   //推挽输出
    gpio_bit_set(GPIOC, GPIO_PIN_0);  //将PE2默认状态设置为高电平

    // 初始化SPI接口
    spi_bus_init();

    /* 读取JEDEC ID */
    jede_id = spi_flash_read_jedec_id();
    if (jede_id)
    {
        BSP_LOG("flash JEDEID = 0x%4X\r\n", jede_id);
    }
    
    /* 读设备ID */
    device_id = spi_flash_read_device_id();
    if (device_id)
    {
        BSP_LOG("flash Device ID = 0x%2X\r\n", device_id);
    }
    
    /* 获取芯片信息 */
    ret = spi_flash_information(&_flash);
    if (TRUE == ret)
    {
        /* 打印信息芯片信息 */
        BSP_LOG("spi flash manufact dev id: 0x%4X\r\n", _flash.manufact_dev_id);
        BSP_LOG("spi flash name: %s\r\n", _flash.name);
        BSP_LOG("spi flash page size: %d\r\n", _flash.page_size);
        BSP_LOG("spi flash sector size: %d\r\n", _flash.sector_size);
        BSP_LOG("spi flash total size: %d MB\r\n", (_flash.total_size / 1024 / 1024));

        /* SPI FLASH为W25Q256, 必须使能4字节地址模式 */
        if (W25Q256_MANUFACT_DEV_ID == _flash.manufact_dev_id)
        {
            /* 读取状态寄存器3，判断地址模式 */
            status_reg = spi_flash_read_status_reg(FLASH_STATUS_REG3);       

            /* 如果不是4字节地址模式,则进入4字节地址模式 */
            if ((status_reg & 0X01) == 0)  
            {
                spi_flash_write_enable();  /* 写使能 */
                status_reg |= 1 << 1;  /* ADP=1, 上电4位地址模式 */
                
                spi_flash_write_status_reg(FLASH_STATUS_REG3, status_reg);  /* 写SR3 */
                bsp_delay_ms(20);  /* 不加延时第一次上电会出错 */  
                
                SPI_CS_0();
                flash_rw_byte(CMD_EANBLE_4BYTE_ADDR);  /* 使能4字节地址指令 */
                SPI_CS_1();
            }
        }
    }
    else
    {
        _flash.manufact_dev_id = 0;
    }

    return ret;
}

